# -*- coding: utf-8 -*-
# EvaluatedAlgorithm.py
from RecommenderMetrics import RecommenderMetrics
from EvaluationData import EvaluationData


class EvaluatedAlgorithm:
    """
    封装推荐算法的评估方法。
    """

    def __init__(self, algorithm, name: str):
        """
        初始化 EvaluatedAlgorithm 类。

        :param algorithm: 推荐算法实例。
        :param name: 算法名称。
        """
        self.algorithm = algorithm  # 有 fit 方法
        self.name = name

    def Evaluate(self, evaluationData: EvaluationData, doTopN: bool, n: int = 10, verbose: bool = True) -> dict:
        """
        评估推荐算法的性能。

        :param evaluationData: 包含评估所需数据的 EvaluationData 实例。
        :param doTopN: 是否进行 Top-N 推荐评估。
        :param n: Top-N 推荐的数量，默认为 10。
        :param verbose: 是否打印详细信息，默认为 True。
        :return: 包含评估指标的字典。
        """
        metrics = {}

        # 计算准确性
        if verbose:
            print("Evaluating accuracy...")
        # 拟合训练集
        self.algorithm.fit(evaluationData.GetTrainSet())
        # 在测试集上进行预测
        predictions = self.algorithm.test(evaluationData.GetTestSet())
        # 计算均方根误差和平均绝对误差
        metrics["RMSE"] = RecommenderMetrics.RMSE(predictions)
        metrics["MAE"] = RecommenderMetrics.MAE(predictions)

        if doTopN:
            # 使用 Leave-One-Out 测试评估 Top-N 推荐
            if verbose:
                print("Evaluating top-N with leave-one-out...")
            # 拟合留一验证训练集
            self.algorithm.fit(evaluationData.GetLOOCVTrainSet())
            # 测试集上生成预测
            leftOutPredictions = self.algorithm.test(evaluationData.GetLOOCVTestSet())

            # 为所有不在训练集中的评分生成预测 (留k验证)
            allPredictions = self.algorithm.test(evaluationData.GetLOOCVAntiTestSet())

            # 计算每个用户的 Top-N 推荐
            topNPredicted = RecommenderMetrics.GetTopN(allPredictions, n)

            if verbose:
                print("Computing hit-rate and rank metrics...")

            # 计算命中率
            metrics["HR"] = RecommenderMetrics.HitRate(topNPredicted, leftOutPredictions)

            # 计算累积命中率
            metrics["CHR"] = RecommenderMetrics.CumulativeHitRate(topNPredicted, leftOutPredictions)

            # 计算平均倒数命中率
            metrics["ARHR"] = RecommenderMetrics.AverageReciprocalHitRank(topNPredicted, leftOutPredictions)

            # 使用完整训练集评估推荐属性
            if verbose:
                print("Computing recommendations with full data set...")
            self.algorithm.fit(evaluationData.GetFullTrainSet())
            allPredictions = self.algorithm.test(evaluationData.GetFullAntiTestSet())
            topNPredicted = RecommenderMetrics.GetTopN(allPredictions, n)

            if verbose:
                print("Analyzing coverage, diversity, and novelty...")

            # 计算用户覆盖率（最低预测评分为 4.0）
            metrics["Coverage"] = RecommenderMetrics.UserCoverage(topNPredicted,
                                                                  evaluationData.GetFullTrainSet().n_users,
                                                                  ratingThreshold=4.0)

            # 计算推荐结果的多样性
            metrics["Diversity"] = RecommenderMetrics.Diversity(topNPredicted, evaluationData.GetSimilarities())

            # 计算推荐结果的新颖性
            metrics["Novelty"] = RecommenderMetrics.Novelty(topNPredicted,
                                                            evaluationData.GetPopularityRankings())

        if verbose:
            print("Analysis complete.")

        return metrics

    def GetName(self) -> str:
        """
        获取算法名称。

        :return: 算法名称。
        """
        return self.name

    def GetAlgorithm(self):
        """
        获取算法实例
        :return:
        """
        return self.algorithm
